package com.mz.mattress.client.plan;

import com.mz.mattress.api.entity.PlanTaskData;
import com.mz.mattress.api.enums.TaskStatusEnum;
import com.mz.mattress.api.redis.RedisKeys;
import com.mz.mattress.client.mapper.PlanTaskDataMapper;
import com.mz.mattress.api.common.StepBean;
import com.mz.mattress.client.task.TaskStep;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.Nullable;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * 任务步骤处理器
 * 单例
 */
@Slf4j
public abstract class AbstractPlanExecutor implements PlanExecutor {

    private final PlanTaskDataMapper planTaskDataMapper;

    public AbstractPlanExecutor(PlanTaskDataMapper planTaskDataMapper) {
        this.planTaskDataMapper = planTaskDataMapper;
    }

    public void doTask(PlanTaskData planTaskData) {
        start(planTaskData);
    }

    private void start(PlanTaskData data) {

        if (data == null) {
            return;
        }

        //判断任务能不能执行
        if (data.isEnd()) {
            log.info("task_end_taskId={}", data.getId());
            return;
        }

        List<StepBean> stepBeanList = getStepBeans();
        if (stepBeanList == null) return;

        data.setStepList(stepBeanList);

        while (!data.isEnd()) {
            StepBean stepBean = data.queryCurrStep();
            if (stepBean == null) {
                data.nextStep();
                continue;
            }

            doHandler(stepBean.getMethod(), data);

            if (data.isError()) {
                log.info("task_error_taskId={}", data.getId());
                break;
            }

            long delayTime = stepBean.getDelayTime();
            if (delayTime > 0) {

                //放入延迟队列
                BeanHolder.getRedisTemplate().opsForZSet().
                        add(RedisKeys.PLAN_TASK_SCORE_QUEUE, data.getId(),
                                (delayTime * 1000) + System.currentTimeMillis());

                log.info("task_delay_taskId={},delay={}", data.getId(), delayTime);
                return;
            }

        }

        log.info("task_end_taskId={}", data.getId());

    }

    private @Nullable List<StepBean> getStepBeans() {
        Method[] methodArr = this.getClass().getDeclaredMethods();
        if (methodArr.length < 1) {
            log.error("获取不到类方法,丢弃任务");
            return null;
        }

        List<StepBean> stepBeanList = new ArrayList<>();
        for (Method method : methodArr) {
            TaskStep step = method.getAnnotation(TaskStep.class);
            if (step == null) {
                continue;
            }

            StepBean stepBean = new StepBean();
            stepBean.setNum(step.value());
            stepBean.setMethod(method);
            stepBean.setDelayTime(step.delayTime());
            stepBeanList.add(stepBean);
        }
        return stepBeanList;
    }

    private void doHandler(Method method, PlanTaskData data) {
        try {
            method.invoke(this, data);
            data.nextStep();
            saveToDb(data);
        } catch (Throwable e) {
            log.error("plan_doHandler_error_id={}", data.getId(), e);
            data.setStatus(TaskStatusEnum.FAILED.getValue());
            saveToDb(data);
        }

    }

    public void saveToDb(PlanTaskData taskData) {
//        if (taskData.getParams() != null) {
//            taskData.setParams(JacksonUtils.toJson(taskData.getParams()));
//        }
        planTaskDataMapper.updateById(taskData);

    }

}
